package com.huawei.spare.part.replace.utils;

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.validation.Configuration;
import lombok.Getter;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.sf.expectit.Expect;
import net.sf.expectit.ExpectBuilder;
import net.sf.expectit.MultiResult;
import net.sf.expectit.Result;
import net.sf.expectit.matcher.Matcher;
import org.slf4j.Logger;

import static com.huawei.spare.part.replace.Constants.ResponseNotExecuted;
import static net.sf.expectit.filter.Filters.removeColors;
import static net.sf.expectit.filter.Filters.removeNonPrintable;
import static net.sf.expectit.matcher.Matchers.contains;

@Getter
public class SimplePTY implements Closeable {

  private static final Logger log = org.slf4j.LoggerFactory.getLogger(SimplePTY.class);
  {
  }

  public static final String CHARSET = "UTF-8";

  SSHClient sshClient;
  Session session;
  Session.Shell shell;
  private Expect interact;

  private DefaultConfig getKeepAliveConfig() {
    DefaultConfig config = new DefaultConfig();
    config.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
    return config;
  }

  public static SimplePTY allocate(SSHClient sshClient) throws IOException {
    SimplePTY $this = new SimplePTY();
    $this.sshClient = sshClient;
    $this.session = $this.sshClient.startSession();
    $this.session.allocateDefaultPTY();
    $this.shell = $this.session.startShell();

    $this.listen();
    return $this;
  }

  public static SimplePTY allocate(String hostname, String username, String password) throws IOException {
    SimplePTY $this = new SimplePTY();
    $this.sshClient = new SSHClient();
    //$this.sshClient = new SSHClient($this.getKeepAliveConfig());
    $this.sshClient.addHostKeyVerifier(new PromiscuousVerifier());
    $this.sshClient.connect(hostname);
    $this.sshClient.authPassword(username, password);
    $this.sshClient.getConnection().getKeepAlive().setKeepAliveInterval(10);

    $this.session = $this.sshClient.startSession();
    $this.session.allocateDefaultPTY();
    $this.shell = $this.session.startShell();

    $this.listen();
    return $this;
  }

  public void listen() throws IOException {
    this.interact = new ExpectBuilder()
        .withOutput(shell.getOutputStream())
        .withInputs(shell.getInputStream(), shell.getErrorStream())
        //.withEchoInput(System.out)
        .withEchoInput(new AppendableAdapter())
        //.withEchoOutput(System.err)
        .withInputFilters(removeColors(), removeNonPrintable())
        .withExceptionOnFailure()
        .withAutoFlushEcho(true)
        .build();
  }

  public Expect sendLine(String string) throws IOException {
    return getInteract().sendLine(string);
  }

  public Expect send(String string) throws IOException {
    return getInteract().send(string);
  }

  public <R extends Result> R expect(Matcher<R> matcher) throws IOException {
    return getInteract().expect(matcher);
  }

  public MultiResult expect(Matcher<?>... matchers) throws IOException {
    return getInteract().expect(matchers);
  }

  public <R extends Result> R expect(Integer timeout, Matcher<R> matcher) throws IOException, InterruptedException {
    getInteract().withTimeout(timeout, TimeUnit.SECONDS);
    return getInteract().expect(matcher);
  }

  public MultiResult expect(Integer timeout, Matcher<?>... matchers) throws IOException {
    return getInteract().expect(matchers);
  }


  public void closeSession() {
    IOUtils.closeQuietly(interact);
    IOUtils.closeQuietly(shell);
    IOUtils.closeQuietly(session);
  }

  @Override
  public void close() {
    closeSession();
    IOUtils.closeQuietly(sshClient);
  }

  public static class AppendableAdapter implements Appendable {

    @Override public Appendable append(CharSequence csq) throws IOException {
      log.info(csq.toString());
      return this;
    }

    @Override
    public Appendable append(CharSequence csq, int start, int end) throws IOException {
      throw new UnsupportedOperationException();
    }

    @Override
    public Appendable append(char c) throws IOException {
      throw new UnsupportedOperationException();
    }
  }

  public static void main(String[] args) throws IOException, InterruptedException {
    log.info("test");
    try (SimplePTY pty = SimplePTY.allocate("112.93.129.107", "hy", "Huawei@1234")) {
      pty.expect(contains("SMM:"));
      pty.sendLine("telnet 0 1101");
      final Result do_you_want_to_continue = pty.expect(contains("user name:"));
      pty.send("hy\r\n");
      pty.expect(contains("PassWord:"));
      pty.send("Huawei@1234\r\n");
      pty.expect(contains("Please input your choice:"));
      pty.send("36\r\n");
      pty.expect(contains("COM#(1 ~ 3):"));
      pty.send("2");
      pty.send("");
      pty.expect(contains("PassWord:"));

      //pty.sendLine("smmset -l swi4:fru1 -d powerstate -v poweroff");
      //pty.getInteract().interact()
      //    .when(contains("Do you want to continue")).then((e) -> {
      //  pty.sendLine("Y");
      //}).when(contains("Success")).then((r) -> {
      //  System.out.println("success");
      //  pty.getInteract().close();
      //}).when(contains(ResponseNotExecuted)).then((r) -> {
      //  System.out.println("failed");
      //  pty.send("PTYShell:Exit");
      //}).until(contains("PTYShell:Exit"));

      //pty.expect(contains("Ubuntu"));
      //pty.sendLine("python /root/spare.py");
      //pty.expect(contains("spare.py"));
      //
      //pty.sendLine("1");
      //final Result expect = pty.expect(contains("2"));
      //
      //pty.sendLine("2");
      //pty.expect(contains("2"));
      //
      //pty.sendLine("3");
      //pty.expect(contains("3"));
      //
      //pty.interact.close();
      //System.out.println();
    }
  }
}
